﻿namespace Newtonsoft.Json.Converters
{
    using Newtonsoft.Json;
    using Newtonsoft.Json.Utilities;
    using System;
    using System.Collections.Generic;
    using System.Runtime.InteropServices;
    using System.Xml;

    public class XmlNodeConverter : JsonConverter
    {
        private const string CDataName = "#cdata-section";
        private const string CommentName = "#comment";
        private const string DeclarationName = "?xml";
        private const string SignificantWhitespaceName = "#significant-whitespace";
        private const string TextName = "#text";
        private const string WhitespaceName = "#whitespace";

        public override bool CanConvert(Type valueType)
        {
            return typeof(XmlNode).IsAssignableFrom(valueType);
        }

        private void DeserializeNode(JsonReader reader, XmlDocument document, XmlNamespaceManager manager, XmlNode currentNode)
        {
            do
            {
                switch (reader.TokenType)
                {
                    case JsonToken.EndObject:
                    case JsonToken.EndArray:
                        return;

                    case JsonToken.PropertyName:
                        {
                            string propertyName = reader.Value.ToString();
                            reader.Read();
                            if (reader.TokenType != JsonToken.StartArray)
                            {
                                this.DeserializeValue(reader, document, manager, propertyName, currentNode);
                                break;
                            }
                            while (reader.Read() && (reader.TokenType != JsonToken.EndArray))
                            {
                                this.DeserializeValue(reader, document, manager, propertyName, currentNode);
                            }
                            break;
                        }
                    default:
                        throw new JsonSerializationException("Unexpected JsonToken when deserializing node: " + reader.TokenType);
                }
            }
            while ((reader.TokenType == JsonToken.PropertyName) || reader.Read());
        }

        private void DeserializeValue(JsonReader reader, XmlDocument document, XmlNamespaceManager manager, string propertyName, XmlNode currentNode)
        {
            bool flag2;
            string prefix;
            Dictionary<string, string> dictionary;
            switch (propertyName)
            {
                case "#text":
                    currentNode.AppendChild(document.CreateTextNode(reader.Value.ToString()));
                    return;

                case "#cdata-section":
                    currentNode.AppendChild(document.CreateCDataSection(reader.Value.ToString()));
                    return;

                case "#whitespace":
                    currentNode.AppendChild(document.CreateWhitespace(reader.Value.ToString()));
                    return;

                case "#significant-whitespace":
                    currentNode.AppendChild(document.CreateSignificantWhitespace(reader.Value.ToString()));
                    return;
            }
            if (propertyName[0] != '?')
            {
                bool flag = false;
                flag2 = false;
                prefix = this.GetPrefix(propertyName);
                dictionary = new Dictionary<string, string>();
                if ((((reader.TokenType != JsonToken.String) && (reader.TokenType != JsonToken.Null)) && ((reader.TokenType != JsonToken.Boolean) && (reader.TokenType != JsonToken.Integer))) && ((reader.TokenType != JsonToken.Float) && (reader.TokenType != JsonToken.Date)))
                {
                    while ((!flag && !flag2) && reader.Read())
                    {
                        JsonToken tokenType = reader.TokenType;
                        if (tokenType != JsonToken.PropertyName)
                        {
                            if (tokenType != JsonToken.EndObject)
                            {
                                throw new JsonSerializationException("Unexpected JsonToken: " + reader.TokenType);
                            }
                        }
                        else
                        {
                            string key = reader.Value.ToString();
                            if (key[0] == '@')
                            {
                                string str7;
                                key = key.Substring(1);
                                reader.Read();
                                string str6 = reader.Value.ToString();
                                dictionary.Add(key, str6);
                                if (this.IsNamespaceAttribute(key, out str7))
                                {
                                    manager.AddNamespace(str7, str6);
                                }
                            }
                            else
                            {
                                flag = true;
                            }
                            continue;
                        }
                        flag2 = true;
                    }
                }
            }
            else
            {
                if (!(propertyName == "?xml"))
                {
                    XmlProcessingInstruction instruction = document.CreateProcessingInstruction(propertyName.Substring(1), reader.Value.ToString());
                    currentNode.AppendChild(instruction);
                    return;
                }
                string version = null;
                string encoding = null;
                string standalone = null;
                while (reader.Read() && (reader.TokenType != JsonToken.EndObject))
                {
                    string str10 = reader.Value.ToString();
                    if (str10 == null)
                    {
                        goto Label_0152;
                    }
                    if (!(str10 == "@version"))
                    {
                        if (str10 == "@encoding")
                        {
                            goto Label_0128;
                        }
                        if (str10 == "@standalone")
                        {
                            goto Label_013D;
                        }
                        goto Label_0152;
                    }
                    reader.Read();
                    version = reader.Value.ToString();
                    continue;
                Label_0128:
                    reader.Read();
                    encoding = reader.Value.ToString();
                    continue;
                Label_013D:
                    reader.Read();
                    standalone = reader.Value.ToString();
                    continue;
                Label_0152:
                    throw new JsonSerializationException("Unexpected property name encountered while deserializing XmlDeclaration: " + reader.Value);
                }
                XmlDeclaration declaration = document.CreateXmlDeclaration(version, encoding, standalone);
                currentNode.AppendChild(declaration);
                return;
            }
            XmlElement newChild = !string.IsNullOrEmpty(prefix) ? document.CreateElement(propertyName, manager.LookupNamespace(prefix)) : document.CreateElement(propertyName);
            currentNode.AppendChild(newChild);
            foreach (KeyValuePair<string, string> pair in dictionary)
            {
                string str8 = this.GetPrefix(pair.Key);
                XmlAttribute newAttr = !string.IsNullOrEmpty(str8) ? document.CreateAttribute(pair.Key, manager.LookupNamespace(str8)) : document.CreateAttribute(pair.Key);
                newAttr.Value = pair.Value;
                newChild.SetAttributeNode(newAttr);
            }
            if (reader.TokenType == JsonToken.String)
            {
                newChild.AppendChild(document.CreateTextNode(reader.Value.ToString()));
            }
            else if (reader.TokenType == JsonToken.Integer)
            {
                newChild.AppendChild(document.CreateTextNode(XmlConvert.ToString((long)reader.Value)));
            }
            else if (reader.TokenType == JsonToken.Float)
            {
                newChild.AppendChild(document.CreateTextNode(XmlConvert.ToString((double)reader.Value)));
            }
            else if (reader.TokenType == JsonToken.Boolean)
            {
                newChild.AppendChild(document.CreateTextNode(XmlConvert.ToString((bool)reader.Value)));
            }
            else if (reader.TokenType == JsonToken.Date)
            {
                newChild.AppendChild(document.CreateTextNode(XmlConvert.ToString((DateTime)reader.Value)));
            }
            else if ((reader.TokenType != JsonToken.Null) && !flag2)
            {
                manager.PushScope();
                this.DeserializeNode(reader, document, manager, newChild);
                manager.PopScope();
            }
        }

        private string GetPrefix(string qualifiedName)
        {
            int index = qualifiedName.IndexOf(':');
            if (((index != -1) && (index != 0)) && ((qualifiedName.Length - 1) != index))
            {
                return qualifiedName.Substring(0, index);
            }
            return string.Empty;
        }

        private string GetPropertyName(XmlNode node)
        {
            switch (node.NodeType)
            {
                case XmlNodeType.Element:
                    return node.Name;

                case XmlNodeType.Attribute:
                    return ("@" + node.Name);

                case XmlNodeType.Text:
                    return "#text";

                case XmlNodeType.CDATA:
                    return "#cdata-section";

                case XmlNodeType.ProcessingInstruction:
                    return ("?" + node.Name);

                case XmlNodeType.Comment:
                    return "#comment";

                case XmlNodeType.Whitespace:
                    return "#whitespace";

                case XmlNodeType.SignificantWhitespace:
                    return "#significant-whitespace";

                case XmlNodeType.XmlDeclaration:
                    return "?xml";
            }
            throw new JsonSerializationException("Unexpected XmlNodeType when getting node name: " + node.NodeType);
        }

        private bool IsNamespaceAttribute(string attributeName, out string prefix)
        {
            if (attributeName.StartsWith("xmlns", StringComparison.Ordinal))
            {
                if (attributeName.Length == 5)
                {
                    prefix = string.Empty;
                    return true;
                }
                if (attributeName[5] == ':')
                {
                    prefix = attributeName.Substring(6, attributeName.Length - 6);
                    return true;
                }
            }
            prefix = null;
            return false;
        }

        public override object ReadJson(JsonReader reader, Type objectType)
        {
            if (objectType != typeof(XmlDocument))
            {
                throw new JsonSerializationException("XmlNodeConverter only supports deserializing XmlDocuments");
            }
            XmlDocument document = new XmlDocument();
            XmlNamespaceManager manager = new XmlNamespaceManager(document.NameTable);
            reader.Read();
            this.DeserializeNode(reader, document, manager, document);
            return document;
        }

        private void SerializeGroupedNodes(JsonWriter writer, XmlNode node)
        {
            Dictionary<string, List<XmlNode>> dictionary = new Dictionary<string, List<XmlNode>>();
            for (int i = 0; i < node.ChildNodes.Count; i++)
            {
                List<XmlNode> list;
                XmlNode node2 = node.ChildNodes[i];
                string propertyName = this.GetPropertyName(node2);
                if (!dictionary.TryGetValue(propertyName, out list))
                {
                    list = new List<XmlNode>();
                    dictionary.Add(propertyName, list);
                }
                list.Add(node2);
            }
            foreach (KeyValuePair<string, List<XmlNode>> pair in dictionary)
            {
                List<XmlNode> list2 = pair.Value;
                if (list2.Count == 1)
                {
                    this.SerializeNode(writer, list2[0], true);
                }
                else
                {
                    writer.WritePropertyName(pair.Key);
                    writer.WriteStartArray();
                    for (int j = 0; j < list2.Count; j++)
                    {
                        this.SerializeNode(writer, list2[j], false);
                    }
                    writer.WriteEndArray();
                }
            }
        }

        private void SerializeNode(JsonWriter writer, XmlNode node, bool writePropertyName)
        {
            switch (node.NodeType)
            {
                case XmlNodeType.Element:
                    if (writePropertyName)
                    {
                        writer.WritePropertyName(node.Name);
                    }
                    if ((CollectionUtils.IsNullOrEmpty(node.Attributes) && (node.ChildNodes.Count == 1)) && (node.ChildNodes[0].NodeType == XmlNodeType.Text))
                    {
                        writer.WriteValue(node.ChildNodes[0].Value);
                        return;
                    }
                    if ((node.ChildNodes.Count == 0) && CollectionUtils.IsNullOrEmpty(node.Attributes))
                    {
                        writer.WriteNull();
                        return;
                    }
                    writer.WriteStartObject();
                    for (int i = 0; i < node.Attributes.Count; i++)
                    {
                        this.SerializeNode(writer, node.Attributes[i], true);
                    }
                    this.SerializeGroupedNodes(writer, node);
                    writer.WriteEndObject();
                    return;

                case XmlNodeType.Attribute:
                case XmlNodeType.Text:
                case XmlNodeType.CDATA:
                case XmlNodeType.ProcessingInstruction:
                case XmlNodeType.Whitespace:
                case XmlNodeType.SignificantWhitespace:
                    if (writePropertyName)
                    {
                        writer.WritePropertyName(this.GetPropertyName(node));
                    }
                    writer.WriteValue(node.Value);
                    return;

                case XmlNodeType.Comment:
                    if (writePropertyName)
                    {
                        writer.WriteComment(node.Value);
                    }
                    return;

                case XmlNodeType.Document:
                case XmlNodeType.DocumentFragment:
                    this.SerializeGroupedNodes(writer, node);
                    return;

                case XmlNodeType.XmlDeclaration:
                    {
                        XmlDeclaration declaration = (XmlDeclaration)node;
                        writer.WritePropertyName(this.GetPropertyName(node));
                        writer.WriteStartObject();
                        if (!string.IsNullOrEmpty(declaration.Version))
                        {
                            writer.WritePropertyName("@version");
                            writer.WriteValue(declaration.Version);
                        }
                        if (!string.IsNullOrEmpty(declaration.Encoding))
                        {
                            writer.WritePropertyName("@encoding");
                            writer.WriteValue(declaration.Encoding);
                        }
                        if (!string.IsNullOrEmpty(declaration.Standalone))
                        {
                            writer.WritePropertyName("@standalone");
                            writer.WriteValue(declaration.Standalone);
                        }
                        writer.WriteEndObject();
                        return;
                    }
            }
            throw new JsonSerializationException("Unexpected XmlNodeType when serializing nodes: " + node.NodeType);
        }

        public override void WriteJson(JsonWriter writer, object value)
        {
            XmlNode node = value as XmlNode;
            if (node == null)
            {
                throw new ArgumentException("Value must be an XmlNode", "value");
            }
            writer.WriteStartObject();
            this.SerializeNode(writer, node, true);
            writer.WriteEndObject();
        }
    }
}

